<?php
namespace zake\foundation\able\traits;

use zake\foundation\able\ProtocolAble;
use zake\foundation\stream\BufferReadStream;

/**
 * Trait ReadBufferTrait
 * @package zake\foundation\able\traits
 *
 * @author liu.bin 2020-04-28 15:53
 */
trait ReadBufferTrait
{

    /**
     * 读缓冲区
     * @var string
     */
    protected $readBuffer = "";


    /**
     * 当前包长度
     * @var int
     */
    protected $packageLength = 0;


    /**
     * 读到缓冲区
     * @param ProtocolAble $protocol
     * @param \Closure $callback
     * @param \Closure|null $closeCallBack
     *
     * @author liu.bin 2020-04-28 15:50
     */
    public function readBuffer(ProtocolAble $protocol,\Closure $callback,$closeCallBack = null)
    {

        // 从socket中读取数据流
        $buffer = $this->readOnce();


        // 客户端关闭
        if(false === $buffer){
            $this->close($closeCallBack);
            return;
        }

        // 当前缓冲区字节长度
        $this->readBuffer .= $buffer;

        // 定义变量
        $readBuffer = $this->readBuffer;
        $length     = strlen($readBuffer);


        // 数据包超过最大长度
        if($length > BufferReadStream::READ_MAX_LENGTH){
            $this->close($closeCallBack);
            return;
        }


        // 循环处理粘包(一次性读取多个包)
        while (true){


            // 读取当前包长度
            $frameLength = $protocol->framePoint($this->getReadBuffer(),$this);

            // false:关闭当前链接
            if(false === $frameLength){
                $this->close($closeCallBack);
                break;
            }

            //
            if(0 === $frameLength){
                break;
            }

            // 定义当前包长度
            $this->setPackageLength($frameLength);

            // 读取当前包数据(null:当前缓冲区长度不满足包长度)
            $frameBuffer = $this->shiftReadBuffer($frameLength);
            if(null == $frameBuffer){
                $this->close($closeCallBack);
                break;
            }

            // 执行回调，处理业务
            $callback($frameBuffer);
            $this->setPackageLength(0);

            // 如果当前数据清空，需要跳出当前循环，否则framePoint会返回false,断开连接
            if(empty($this->getReadBuffer())){
                break;
            }

        }

    }


    /**
     * 获取读缓冲区,不会刷新缓冲区
     *
     * @return string
     * @author liu.bin 2020-03-24 13:56
     */
    public function getReadBuffer()
    {
        return $this->readBuffer;
    }


    /**
     * 读取读缓冲区的数据并刷新缓冲区
     *
     * size:
     *      非null:
     *          1- 满足长度，则读出缓冲数据
     *          2- 不满足长度，返回null
     *      null:
     *          1- 直接读出所有缓冲区数据
     *
     *
     * @param int|null $size 需要读取缓冲区的长度。如果null，则为当前包长
     * @return string
     * @author liu.bin 2020-03-27 10:26
     */
    public function shiftReadBuffer($size = null)
    {

        // 定义变量
        $readBuffer = $this->readBuffer;
        $length     = strlen($readBuffer);


        // 读出多有缓冲区数据
        if(is_null($size)){
            $this->resetReadBuffer();
            $shiftBuffer = $readBuffer;
            return $shiftBuffer;
        }


        if($length >= $size){
            $shiftBuffer= substr($readBuffer,0,$size);
            $leftBuffer = substr($readBuffer,$size);
            $this->resetReadBuffer($leftBuffer);
        }else{
            $shiftBuffer = null;
        }

        // 返回读取的数据
        return $shiftBuffer;
    }


    /**
     * 重置读缓冲区数据
     *
     * @param string $buffer
     * @return bool
     * @author liu.bin 2020-03-27 10:33
     */
    public function resetReadBuffer($buffer = "")
    {
        $this->readBuffer = (string)$buffer;
        $this->setPackageLength(0);
        return true;
    }


    /**
     * 获取当前包长
     * @return int
     *
     * @author liu.bin 2020-08-18 17:24
     */
    public function getPackageLength(){
        return $this->packageLength;
    }


    /**
     * 定义当前包长
     * @param $packageLength
     *
     * @author liu.bin 2020-08-18 17:24
     */
    public function setPackageLength($packageLength){
        $this->packageLength = $packageLength;
    }




}